package com.xinchen.ssh.demo.core.dao;

import com.xinchen.ssh.demo.core.entity.base.AbstractEntity;
import com.xinchen.ssh.demo.core.entity.page.Page;
import com.xinchen.ssh.demo.core.entity.page.PageInfo;
import com.xinchen.ssh.demo.core.entity.page.QueryConditions;
import org.hibernate.*;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.ast.QueryTranslatorImpl;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.*;

/**
 * Description：
 *
 * @author Xin Chen
 * @version 1.0
 * @date 2016/12/15 11:03
 * @see 2016/12/15 11:03 Created
 ****************************************/
@Repository
public class HibernateEntityManagerImpl extends HibernateDaoSupport implements HibernateEntityManager {
    public HibernateEntityManagerImpl() {
    }

    @Resource(
            name = "sessionFactory"
    )
    public void setSF(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }

    public void delete(Object entity) {
        this.getHibernateTemplate().delete(entity);
    }

    public void delete(Collection entities) {
        this.getHibernateTemplate().deleteAll(entities);
    }

    public void delete(Class entityClass, Serializable id) {
        this.getHibernateTemplate().delete(this.load(entityClass, id));
    }

    public int executeUpdate(String hql) {
        return this.getHibernateTemplate().bulkUpdate(hql);
    }

    public int executeUpdate(String hql, Object[] params) {
        return this.getHibernateTemplate().bulkUpdate(hql, params);
    }

    public int executeSQL(final String sql, final Object[] params) {
        Integer result = (Integer)this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                SQLQuery sqlQuery = session.createSQLQuery(sql);
                if(params != null) {
                    for(int i = 0; i < params.length; ++i) {
                        sqlQuery.setParameter(i, params[i]);
                    }
                }

                return Integer.valueOf(sqlQuery.executeUpdate());
            }
        });
        return result.intValue();
    }

    public List find(String queryString) {
        return this.getHibernateTemplate().find(queryString);
    }

    public List find(String queryString, Object[] params) {
        return this.getHibernateTemplate().find(queryString, params);
    }

    public List find(String queryString, String parameterName, Object[] params) {
        return this.getHibernateTemplate().findByNamedParam(queryString, parameterName, params);
    }

    public List find(final String queryString, final int start, final int size) {
        List list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(queryString);
                query.setFirstResult(start);
                query.setFetchSize(size);
                query.setMaxResults(size);
                return query.list();
            }
        });
        return list;
    }

    public List find(final String queryString, final Object[] params, final int start, final int size) {
        List list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(queryString);
                if(params != null) {
                    for(int i = 0; i < params.length; ++i) {
                        query.setParameter(i, params[i]);
                    }
                }

                query.setFirstResult(start);
                query.setFetchSize(size);
                query.setMaxResults(size);
                return query.list();
            }
        });
        return list;
    }

    public Object load(Class entityClass, Serializable id) {
        return this.getHibernateTemplate().get(entityClass, id);
    }

    public List loadAll(Class entityClass) {
        return this.getHibernateTemplate().loadAll(entityClass);
    }

    public List loadAll(final Class entityClass, final int start, final int size) {
        List list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Criteria criteria = session.createCriteria(entityClass);
                criteria.setFetchSize(size);
                criteria.setFirstResult(start);
                criteria.setMaxResults(size);
                return criteria.list();
            }
        });
        return list;
    }

    public Object refresh(Object entity) {
        this.getHibernateTemplate().refresh(entity);
        return entity;
    }

    public Serializable save(Object entity) {
        return this.getHibernateTemplate().save(entity);
    }

    public List save(Collection entities) {
        ArrayList result = new ArrayList();
        if(entities != null) {
            Iterator iterator = entities.iterator();

            while(iterator.hasNext()) {
                Object entity = iterator.next();
                result.add(this.save(entity));
            }
        }

        return result;
    }

    public void update(Object entity) {
        this.merge(entity);
    }

    private boolean isObjectExisted(Object entity) {
        String entityName = entity.getClass().getName();
        String hql = " from " + entityName + " where id = " + ((AbstractEntity)entity).getId();
        List result = this.find(hql);
        return result.size() == 1;
    }

    public void merge(Object entity) {
        if(this.isObjectExisted(entity)) {
            this.getHibernateTemplate().merge(entity);
        } else {
            throw new HibernateException("ID是" + ((AbstractEntity)entity).getId() + "的记录不存在，修改失败！");
        }
    }

    public void update(Collection entities) {
        if(entities != null) {
            Iterator iterator = entities.iterator();

            while(iterator.hasNext()) {
                Object entity = iterator.next();
                this.update(entity);
            }
        }

    }

    public void updateWithFile(Object obj) {
        this.getHibernateTemplate().update(obj);
    }

    public Page find(String queryString, PageInfo pageInfo) {
        Page result = new Page();
        List data = this.find(queryString, pageInfo.getStartIndex(), pageInfo.getRowsOfPage());
        result.setData(data);
        result.setPageInfo(pageInfo);
        this.preparePageInfo(queryString, (Object[])null, pageInfo);
        return result;
    }

    public Page find(String queryString, Object[] params, PageInfo pageInfo) {
        Page result = new Page();
        List data = this.find(queryString, params, pageInfo.getStartIndex(), pageInfo.getRowsOfPage());
        result.setData(data);
        result.setPageInfo(pageInfo);
        this.preparePageInfo(queryString, params, pageInfo);
        return result;
    }

    private void preparePageInfo(String queryString, Object[] params, PageInfo pageInfo) {
        String queryStr1 = "";
        String queryStr2 = "";
        String queryStr3 = "";
        int index = queryString.toUpperCase().indexOf("FROM");
        if(index != -1) {
            queryStr1 = queryString.substring(index);
            queryStr2 = "SELECT COUNT(*) " + queryStr1;
            index = queryStr2.toUpperCase().lastIndexOf("ORDER");
            if(index != -1) {
                queryStr3 = queryStr2.substring(0, index);
                this.computePage(queryStr3, params, pageInfo);
            } else {
                this.computePage(queryStr2, params, pageInfo);
            }
        }

    }

    private void computePage(String queryString, Object[] params, PageInfo pageInfo) {
        List rowcount = null;
        if(params != null) {
            rowcount = this.getHibernateTemplate().find(queryString, params);
        } else {
            rowcount = this.getHibernateTemplate().find(queryString);
        }

        if(rowcount != null && rowcount.size() > 0) {
            int count = ((Number)rowcount.get(0)).intValue();
            pageInfo.setTotalRowCount(count);
            if(count % pageInfo.getRowsOfPage() > 0) {
                pageInfo.setTotalPageCount(count / pageInfo.getRowsOfPage() + 1);
            } else {
                pageInfo.setTotalPageCount(count / pageInfo.getRowsOfPage());
            }

            if(count == 0) {
                pageInfo.setTotalPageCount(1);
            }
        }

    }

    public Page loadAll(Class entityClass, PageInfo pageInfo) {
        Page result = new Page();
        List data = this.loadAll(entityClass, pageInfo.getStartIndex(), pageInfo.getRowsOfPage());
        result.setData(data);
        result.setPageInfo(pageInfo);
        String queryString = "SELECT COUNT(*) FROM " + entityClass.getName();
        this.computePage(queryString, (Object[])null, pageInfo);
        return result;
    }

    private Object[] find(final String hql, final String[] propertyNames, final String[] operators, final Object[] values, final int offset, final int size, final boolean isTotalSize, final String orderBy, final String groupBy, final String otherCause) {
        Map map = (Map)this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                StringBuilder countSqlStrBd = new StringBuilder("");
                StringBuilder fullSqlStrBd = new StringBuilder("");
                Integer count = Integer.valueOf(0);
                HashMap map = new HashMap();
                if(hql != null && !hql.trim().equals("")) {
                    StringBuilder whereStrBd = new StringBuilder("");
                    if(hql.toLowerCase().indexOf("where") != -1) {
                        whereStrBd.append(" ");
                    }

                    Query query;
                    if(propertyNames != null && propertyNames.length > 0 && values != null && values.length > 0) {
                        if(propertyNames.length != values.length) {
                            throw new HibernateException("propertyNames length noe equal values length");
                        }

                        if(operators != null && propertyNames.length != operators.length) {
                            throw new HibernateException("propertyNames length noe equal operators length");
                        }

                        int countSql;
                        for(countSql = 0; countSql <= propertyNames.length - 1; ++countSql) {
                            if("".equals(whereStrBd.toString())) {
                                whereStrBd.append(" where ");
                            } else {
                                whereStrBd.append("and ");
                            }

                            if(operators != null && operators[countSql].equalsIgnoreCase("isnull")) {
                                whereStrBd.append(propertyNames[countSql]).append(" is null ");
                            } else if(operators != null && operators[countSql].equalsIgnoreCase("isnotnull")) {
                                whereStrBd.append(propertyNames[countSql]).append(" is not null ");
                            } else if(operators != null && operators[countSql].equalsIgnoreCase("isempty")) {
                                whereStrBd.append(propertyNames[countSql]).append(" = \'\' ");
                            } else if(operators != null && operators[countSql].equalsIgnoreCase("isnotempty")) {
                                whereStrBd.append(propertyNames[countSql]).append(" <> \'\' ");
                            } else if(operators != null && operators[countSql].equalsIgnoreCase("like")) {
                                whereStrBd.append(propertyNames[countSql]).append(" like ? ").append(" escape \'\\\'");
                            } else {
                                whereStrBd.append(propertyNames[countSql]).append(operators != null && operators[countSql] != null?" " + operators[countSql]:"=").append(" ? ");
                            }
                        }

                        fullSqlStrBd.delete(0, fullSqlStrBd.length());
                        fullSqlStrBd.append(hql).append(whereStrBd);
                        fullSqlStrBd.append(orderBy != null && !"".equals(orderBy.trim())?" order by " + orderBy:"");
                        fullSqlStrBd.append(groupBy != null && !"".equals(groupBy.trim())?" group by " + groupBy:"");
                        fullSqlStrBd.append(otherCause != null && !"".equals(otherCause.trim())?" " + otherCause:"");
                        query = session.createQuery(fullSqlStrBd.toString());

                        for(countSql = 0; countSql <= values.length - 1; ++countSql) {
                            if((operators == null || !operators[countSql].equalsIgnoreCase("isnull")) && (operators == null || !operators[countSql].equalsIgnoreCase("isnotnull")) && (operators == null || !operators[countSql].equalsIgnoreCase("isempty")) && (operators == null || !operators[countSql].equalsIgnoreCase("isnotempty"))) {
                                query.setParameter(countSql, values[countSql]);
                            }
                        }
                    } else {
                        fullSqlStrBd.delete(0, fullSqlStrBd.length());
                        fullSqlStrBd.append(hql).append(whereStrBd);
                        fullSqlStrBd.append(orderBy != null && !"".equals(orderBy.trim())?" order by " + orderBy:"");
                        fullSqlStrBd.append(groupBy != null && !"".equals(groupBy.trim())?" group by " + groupBy:"");
                        fullSqlStrBd.append(otherCause != null && !"".equals(otherCause.trim())?" " + otherCause:"");
                        query = session.createQuery(fullSqlStrBd.toString());
                    }

                    if(isTotalSize) {
                        countSqlStrBd.delete(0, countSqlStrBd.length());
                        countSqlStrBd.append(hql).append(whereStrBd);
                        countSqlStrBd.append(groupBy != null && !"".equals(groupBy.trim())?" group by " + groupBy:"");
                        countSqlStrBd.append(otherCause != null && !"".equals(otherCause.trim())?" " + otherCause:"");
                        String var11 = countSqlStrBd.toString();
                        var11 = "select count(1) from(" + HibernateEntityManagerImpl.this.getSqlFromHql(var11) + ")";
                        SQLQuery query2 = session.createSQLQuery(var11);
                        if(values != null) {
                            for(int i = 0; i <= values.length - 1; ++i) {
                                if((operators == null || !operators[i].equalsIgnoreCase("isnull")) && (operators == null || !operators[i].equalsIgnoreCase("isnotnull")) && (operators == null || !operators[i].equalsIgnoreCase("isempty")) && (operators == null || !operators[i].equalsIgnoreCase("isnotempty"))) {
                                    query2.setParameter(i, values[i]);
                                }
                            }
                        }

                        count = Integer.valueOf(query2.list().get(0).toString());
                    }

                    if(offset > 0) {
                        query.setFirstResult(offset);
                    }

                    if(size > 0) {
                        query.setMaxResults(size);
                    }

                    map.put("list", query.list());
                    map.put("count", count);
                    return map;
                } else {
                    throw new SQLException();
                }
            }
        });
        return new Object[]{map.get("list"), map.get("count")};
    }

    private String getSqlFromHql(String hql) {
        QueryTranslatorImpl ts = new QueryTranslatorImpl(hql, hql, Collections.EMPTY_MAP, (SessionFactoryImplementor)super.getSessionFactory());
        ts.compile(Collections.EMPTY_MAP, false);
        return ts.getSQLString();
    }

    public Page find(String hql, QueryConditions condition, PageInfo pageInfo, boolean isTotalSize) {
        Page result = new Page();
        int index = condition.getPropertyNames().size();
        String[] propertyNames = new String[index];
        String[] operators = new String[index];

        for(int objs = 0; objs < index; ++objs) {
            propertyNames[objs] = (String)condition.getPropertyNames().get(objs);
            operators[objs] = (String)condition.getOperators().get(objs);
        }

        if(pageInfo.getCurrentPageNum() < 1) {
            pageInfo.setCurrentPageNum(1);
        }

        Object[] var11 = this.find(hql, propertyNames, operators, condition.getValues().toArray(), (pageInfo.getCurrentPageNum() - 1) * pageInfo.getRowsOfPage(), pageInfo.getRowsOfPage(), isTotalSize, condition.getOrderBy(), condition.getGroupBy(), condition.getOtherHql());
        if(isTotalSize) {
            int count = ((Integer)var11[1]).intValue();
            pageInfo.setTotalRowCount(count);
            if(count % pageInfo.getRowsOfPage() > 0) {
                pageInfo.setTotalPageCount(count / pageInfo.getRowsOfPage() + 1);
            } else {
                pageInfo.setTotalPageCount(count / pageInfo.getRowsOfPage());
            }

            if(count == 0) {
                pageInfo.setTotalPageCount(1);
            }
        }

        result.setData((List)var11[0]);
        result.setPageInfo(pageInfo);
        return result;
    }

    public List find(String hql, QueryConditions condition) {
        int index = condition.getPropertyNames().size();
        String[] propertyNames = new String[index];
        String[] operators = new String[index];

        for(int objs = 0; objs < index; ++objs) {
            propertyNames[objs] = (String)condition.getPropertyNames().get(objs);
            operators[objs] = (String)condition.getOperators().get(objs);
        }

        Object[] var7 = this.find(hql, propertyNames, operators, condition.getValues().toArray(), 0, 0, false, condition.getOrderBy(), condition.getGroupBy(), condition.getOtherHql());
        return (ArrayList)var7[0];
    }

    public List find(String hql, QueryConditions condition, int maxRowNum) {
        int index = condition.getPropertyNames().size();
        String[] propertyNames = new String[index];
        String[] operators = new String[index];

        for(int objs = 0; objs < index; ++objs) {
            propertyNames[objs] = (String)condition.getPropertyNames().get(objs);
            operators[objs] = (String)condition.getOperators().get(objs);
        }

        Object[] var8 = this.find(hql, propertyNames, operators, condition.getValues().toArray(), 0, maxRowNum, false, condition.getOrderBy(), condition.getGroupBy(), condition.getOtherHql());
        return (ArrayList)var8[0];
    }
}
